package com.ccp.dev.system.configuration;

import com.ccp.dev.core.basic.util.ContextUtil;
import com.ccp.dev.core.util.AppUtil;
import com.ccp.dev.core.util.StringUtil;
import com.ccp.dev.system.dao.ResourcesDao;
import com.ccp.dev.system.model.Resources;
import com.ccp.dev.system.model.SubSystem;
import com.ccp.dev.system.model.SysRole;
import com.ccp.dev.system.service.SysRoleService;
import com.ccp.dev.system.util.SubSystemUtil;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 实现的功能。
 * <pre>
 * 1.系统初始化时，构建系统的url和角色映射。
 * 2.并根据当前的url取出url具有的角色权限。
 * </pre>
 * @author y
 */
public class CustomSecurityMetadataSource implements FilterInvocationSecurityMetadataSource , BeanPostProcessor {
    /**
	 * 具有匿名访问权限的url
	 */
	private  HashSet<String> anonymousUrls=new HashSet<>();

	/**
	 * 具有rest风格的url
	 */
	private  HashSet<String> restUrls=new HashSet<>();

	/**
	 * 设置具有匿名访问权限的url
	 * @param anonymousUrls url
	 */
	public  void setAnonymousUrls(HashSet<String> anonymousUrls) {
		this.anonymousUrls = anonymousUrls;
	}

	/**
	 * 设置具有rest风格的url
	 * @param restUrls url
	 */
	public void setRestUrls(HashSet<String> restUrls) {
		this.restUrls = restUrls;
	}

	/**
     * 根据当前的URL返回该url的角色集合。
     * 1.如果当前的URL在匿名访问的URL集合当中时，在当前的角色中添加匿名访问的角色(SysRole.ROLE_CONFIG_ANONYMOUS)。
     * 2.如果当前系统不存在的情况，给当前用户添加一个公共访问的角色(SysRole.ROLE_CONFIG_PUBLIC)。
     * 3.url 和角色映射，url和参数映射，给当前用户添加一个公共的角色(SysRole.ROLE_CONFIG_PUBLIC)。
     * 
     * @param object 调用筛选器
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)throws IllegalArgumentException {
		Collection<ConfigAttribute> configAttribute =new HashSet<>();
		FilterInvocation filterInvocation=(FilterInvocation)object;
    	HttpServletRequest request=filterInvocation.getRequest();

    	String url = request.getRequestURI();
    	url=removeCtx(url,request.getContextPath());
    	//匿名访问的URL
    	if(anonymousUrls.contains(url)){
    		configAttribute.add(SysRole.ROLE_CONFIG_ANONYMOUS);
    		return configAttribute;
    	}


		//根据当前登录用户取到所拥有的系统
		List<SubSystem> subSystemList = SubSystemUtil.getSubSystemList();
		if(subSystemList==null || subSystemList.size()==0) {
			configAttribute.add(SysRole.ROLE_CONFIG_PUBLIC);
			return configAttribute;
		}

    	SysRoleService sysRoleService=AppUtil.getBean(SysRoleService.class);
		ResourcesDao resourcesDao=AppUtil.getBean(ResourcesDao.class);
		String currentUserId = ContextUtil.getCurrentUserId();
		boolean isContainThreeRole = sysRoleService.isContainThreeRole(currentUserId);
		if(isContainThreeRole){
			Map map = new HashMap(2);
			List<SysRole> allRole = sysRoleService.queryByUserId(currentUserId);
			List<SysRole> xtgly = allRole.stream().filter(a -> a.getRoleType().equals(SysRole.DEFAULT_XTGLY)).collect(Collectors.toList());
			List<SysRole> aqbmgly = allRole.stream().filter(a -> a.getRoleType().equals(SysRole.DEFAULT_AQBMGLY)).collect(Collectors.toList());
			List<SysRole> aqsjy = allRole.stream().filter(a -> a.getRoleType().equals(SysRole.DEFAULT_AQSJY)).collect(Collectors.toList());
			if(!xtgly.isEmpty()){
				map.put("roleType",SysRole.DEFAULT_XTGLY);
				map.put("url",url);
				List<Resources> xtglyRes = resourcesDao.queryResourceByRoleType(map);
				if(xtglyRes.isEmpty()){
					configAttribute.add(SysRole.ROLE_CONFIG_NONE);
				}else{
					configAttribute.add(SysRole.ROLE_CONFIG_THREE);
				}
			}else if(!aqbmgly.isEmpty()){
				map.put("roleType",SysRole.DEFAULT_AQBMGLY);
				map.put("url",url);
				List<Resources> aqbmRes = resourcesDao.queryResourceByRoleType(map);
				if(aqbmRes.isEmpty()){
					configAttribute.add(SysRole.ROLE_CONFIG_NONE);
				}else{
					configAttribute.add(SysRole.ROLE_CONFIG_THREE);
				}
			}else if(!aqsjy.isEmpty()){
				map.put("roleType",SysRole.DEFAULT_AQSJY);
				map.put("url",url);
				List<Resources> sjRes = resourcesDao.queryResourceByRoleType(map);
				if(sjRes.isEmpty()){
					configAttribute.add(SysRole.ROLE_CONFIG_NONE);
				}else{
					configAttribute.add(SysRole.ROLE_CONFIG_THREE);
				}
			}
			return configAttribute;
		}

    	//根据访问的url和拥有的子系统获得拥有当前url的角色
    	Map<String, Object> map=sysRoleService.queryUrlRightMapBySubSystemList(subSystemList, url);
    	boolean isUrlExist=(Boolean)map.get("isUrlExist");
    	List<String> roleList=(List<String>)map.get("roleList");
    	// 如果访问的资源没有在平台中注册,添加一个公共角色,否则添加该资源以及子资源对应的角色
    	// 如果访问的资源已经在平台中注册,但没有对应的角色,添加一个ROLE_NONE角色,防止没有分配角色的资源依然可以正常访问(部分页面中请求的资源)
    	if(!isUrlExist ){
    		//如果资源未配置到数据库中，则检测是否REST资源
			for(String targetURL : restUrls){
				String pattern = targetURL+".*";
				//匹配到了当前URL在资源列表中，跳出循环
				if(Pattern.matches(pattern, url)){
					configAttribute.add(SysRole.ROLE_CONFIG_REST);
					return configAttribute;
				}
			}
			configAttribute.add(SysRole.ROLE_CONFIG_PUBLIC);
    		return configAttribute;
    	}
    	else{
    		if(!roleList.isEmpty()){
    			for(String role:roleList){
    	    		configAttribute.add(new SecurityConfig(role));
    	    	}
    		} else {
    			configAttribute.add(SysRole.ROLE_CONFIG_NONE);
    		}
    	}
    	
    	return configAttribute;
    }
	@Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

	@Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
    	return null;
    }
    
	/**
	 * 获取当前URL
	 * @param url url
	 * @param contextPath 基础路径
	 * @return 去掉基础路径后的url
	 */
	private static String removeCtx(String url,String contextPath){
		url=url.trim();
		if(StringUtil.isEmpty(contextPath)) {return url;}
		if(StringUtil.isEmpty(url)) {return "";}
		if(url.startsWith(contextPath)){
			url=url.replaceFirst(contextPath, "");
		}
		return url;
	}

}
